/*
 * Copyright (c) 2015-2018 Sfkj Science And Technology Co.,Ltd.
 * All Rights Reserved.
 * This software is the confidential and proprietary information of
 * Sfkj Science And Technology Co.,Ltd.
 * ("Confidential Information").You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sfkj.
 */
package com.sfkj.walk.service;

import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.sfkj.walk.SystemException;
import com.sfkj.walk.api.user.AuthenticateCommand;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;

import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@Slf4j
public class UserAuthenticationProvider implements AuthenticationProvider {

    private final CommandGateway commandGateway;
    private final UserDetailsChecker authenticationChecks = new AccountStatusUserDetailsChecker();

    public UserAuthenticationProvider(CommandGateway commandGateway) {
        this.commandGateway = commandGateway;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (!supports(authentication.getClass())) {
            return null;
        }
        if (log.isDebugEnabled()) {
            log.debug("Authenticating: " + authentication);
        }

        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
        String username = token.getName();
        String password = String.valueOf(token.getCredentials());

        AuthenticateCommand command = new AuthenticateCommand();
        command.setPrincipal(username);
        command.setPassword(password);
        Future<String> future = commandGateway.send(command);
        String userId;
        try {
            userId = future.get();
        } catch (InterruptedException | ExecutionException e) {
            Throwable rootCause = Throwables.getRootCause(e);
            throw new AuthenticationServiceException(rootCause.getMessage(), rootCause);
        }

        if (Objects.isNull(userId)) {
            throw new AuthenticationServiceException("手机号码或密码错误");
        }

        UserDetails account = org.springframework.security.core.userdetails.User
                .withUsername(userId).password(password)
                .authorities(Lists.newArrayList())
                .build();

        authenticationChecks.check(account);

        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(account,
                authentication.getCredentials(), account.getAuthorities());
        result.setDetails(authentication.getDetails());

        if (log.isDebugEnabled()) {
            log.debug("Authentication successful: {}", result);
        }

        return result;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

}
